﻿using System.Collections.Generic;

namespace NumberToWords
{
    /// <summary>
    /// Helper class that stores and retrieves mappings for letters to numbers.
    /// </summary>
    public class NumberToLetterConverter
    {
        /// <summary>
        /// Builder class for NumberToLetterConverter instances.
        /// </summary>
        public class NumberToLetterConverterBuilder
        {
            /// <summary>
            /// Store the mappings here
            /// </summary>
            private Dictionary<string, string> lookup = new Dictionary<string, string>();

            /// <summary>
            /// Indicate that the given character key string should map to the characters in values
            /// </summary>
            /// <param name="key">the character key</param>
            /// <param name="values">the mapped values</param>
            /// <returns>this builder</returns>
            public NumberToLetterConverterBuilder AddMapping(string key, string values)
            {
                this.lookup[key] = values;
                return this;
            }

            /// <summary>
            /// Return the created NumberToLetterConverter
            /// </summary>
            /// <returns>the constructed converter instance</returns>
            public NumberToLetterConverter Build()
            {
                return new NumberToLetterConverter(this.lookup);
            }
        }

        /// <summary>
        /// Allows lookups from number to letter
        /// </summary>
        private Dictionary<string, string> numberToLetterLookup;
        /// <summary>
        /// Allows lookups from letter to number (reverse lookup)
        /// </summary>
        private Dictionary<string, string> letterToNumberLookup = new Dictionary<string, string>();

        private NumberToLetterConverter(Dictionary<string, string> lookup)
        {
            this.numberToLetterLookup = new Dictionary<string, string>(lookup);
            // Build the reverse lookup
            foreach (string number in this.numberToLetterLookup.Keys)
            {
                foreach (char letter in this.numberToLetterLookup[number])
                {
                    this.letterToNumberLookup[letter.ToString()] = number;
                }
            }
        }

        /// <summary>
        /// Given the character in the number string, determine what letters
        /// are available to represent it
        /// </summary>
        /// <param name="number">the character as a string</param>
        /// <returns>the mapped letters</returns>
        public string GetLettersForNumber(string number)
        {
            return this.numberToLetterLookup[number];
        }

        /// <summary>
        /// Get the number that corresponds to the given letter
        /// </summary>
        /// <param name="letter">the letter to query for a number</param>
        /// <returns>the number that corresponds to the given letter</returns>
        public string GetNumberForLetter(string letter)
        {
            return this.letterToNumberLookup[letter];
        }

        /// <summary>
        /// Get the number that corresponds to the given letter
        /// </summary>
        /// <param name="letter">the letter to query for a number</param>
        /// <returns>the number that corresponds to the given letter</returns>
        public string GetNumberForLetter(char letter)
        {
            return this.letterToNumberLookup[letter.ToString()];
        }

        /// <summary>
        /// Factory method to create a builder for converter instances.
        /// </summary>
        /// <returns>A new builder</returns>
        public static NumberToLetterConverterBuilder CreateBuilder()
        {
            return new NumberToLetterConverterBuilder();
        }

        /// <summary>
        /// Factory method for creating a converter that corresponds to a
        /// standard telephone letter/number mapping.
        /// </summary>
        /// <returns>a converter that maps standard telephone keys</returns>
        public static NumberToLetterConverter CreateStandardPhoneConverter()
        {
            return NumberToLetterConverter.CreateBuilder()
                .AddMapping("2", "ABC")
                .AddMapping("3", "DEF")
                .AddMapping("4", "GHI")
                .AddMapping("5", "JKL")
                .AddMapping("6", "MNO")
                .AddMapping("7", "PQRS")
                .AddMapping("8", "TUV")
                .AddMapping("9", "WXYZ").Build();
        }
    }
}
